package pl.edu.pw.mini.java2.Communicator.Server;

import java.io.DataInputStream;
import java.io.IOException;

/**
 * Klasa odpowiadająca za odbieranie i przetwarzanie 
 * wiadomości nadchodzących od klienta.
 * @author Dymitr Załunin, Kamil Łysik
 */
public class ReaderWorker extends Thread implements UpdateContactListListener {
	private ClientHandler client;
	private DataInputStream input;

	/**
	 * Konstruktor tworzący nową instancję klasy.
	 * @param client Obiekt klasy obsługującej klienta. 
	 * @param input Strumień wejściowy,
	 */
	public ReaderWorker(ClientHandler client, DataInputStream input) {
		this.client = client;
		this.input = input;
	}

	@Override
	public void run() {
		// Main loop for reading messages
		// and dispatch them
		try {
			while (true) {
				Message m = Message.read(input);
				ServerLogger.print("Reader for " + client.getName()
						+ " got message type: " + m.getType());
				handleMessage(m);
			}
		} catch (Exception e) {
			ServerLogger.print("Reader for " + client.getName()
					+ " got exception: " + e.toString());
			client.Disconnect();
		}
		ServerLogger.print("Reader for " + client.getName() + " finished job.");
	}

	/**
	 * Default message handler
	 * 
	 * @param m
	 *            message
	 */
	private void handleMessage(Message m) {
		switch (m.getType()) {
		case Message.LOGIN_REQUEST:
			HandleLoginRequest(m);
			ClientsManager.Instance().setUpdateListHandler(this);
			break;
		case Message.OK:
		case Message.ERROR:
			break;
		case Message.GET_AVAILABLE_CONTACT:
			ClientsManager.Instance().UpdateHandler(this, client.getName());
			break;
		case Message.CHAT_REQUEST:
			String loginToConn = new String(m.getMessage());
			HandleChatRequest(loginToConn);
			break;
		case Message.CHAT_REJECT:
			HandleChatReject();
			break;
		case Message.TEXT_MESSAGE:
			HandleTextRequest(client.getPair(), m);
			break;
		case Message.VOICE_MESSAGE:
			HandleVoiceRequest(client.getPair(), m);
			break;
		default:

			break;
		}
	}

	private void HandleChatReject() {
		synchronized (ClientsManager.Instance()) {
			client.setIsBusy(false);
			if (client.getPair() == null)
				return;
			client.getPair().setIsBusy(false);
			try {
				client.getPair().getWriter().sendMessage(
						new Message(Message.CHAT_REJECT, "".getBytes()));
			} catch (Exception e) {
			}
			client.getPair().setPair(null);
			client.setPair(null);
		}
	}

	private void HandleLoginRequest(Message m) {
		try {
			client.Login(new String(m.getMessage()));
			ServerLogger.print("Client " + client.getName() + " logged in");
			SendToClient(Message.OK, "");
		} catch (Exception exception) {
			SendToClient(Message.ERROR, "Login failed");
		}
	}

	public void close() {
		try {
			input.close();
		} catch (IOException ignored) {
		}
	}

	/**
	 * 
	 * @param pair
	 *            - [String] Talk-partner
	 */
	private void HandleChatRequest(String pair) {
		if (pair.equals(client.getName())) {
			SendToClient(Message.ERROR, "Nie możesz połączyć się sam z sobą!");
			return;
		}

		ServerLogger.print("Get CHAT_REQUEST from: " + client.getName()
				+ " with " + pair);

		ClientHandler pairHandler = ClientsManager.Instance().FindClient(pair);

		if (pairHandler == null) {
			SendToClient(Message.ERROR, "Nie ma takiego klienta!");
			return;
		}
		synchronized (ClientsManager.Instance()) {
			client.setIsBusy(true);
			if (pairHandler.getIsBusy()) {
				// Error
				client.setIsBusy(false);
				SendToClient(Message.ERROR, "Klient " + pairHandler.getName()
						+ " jest zajety!");
				return;
			}
			pairHandler.setIsBusy(true);
			pairHandler.setPair(client);
			client.setPair(pairHandler);
		}
		client.getWriter().sendMessage(new Message(Message.OK));
		pairHandler.getWriter().sendMessage(
				new Message(Message.CHAT_INVITATION, client.getName()
						.getBytes()));
	}

	private void HandleTextRequest(ClientHandler pair, Message m) {
		if (pair == null) {
			SendToClient(Message.ERROR,
					"Nie jesteś polączony z żadnym klientem!");
			return;
		} else {
			ServerLogger.print("Klient: " + client.getName()
					+ " wysłał  wiadomość: " + new String(m.getMessage())
					+ "\t do " + client.getPair().getName());
			pair.getWriter().sendMessage(m);
		}
	}

	private void HandleVoiceRequest(ClientHandler pair, Message m) {
		if (pair == null) {
			SendToClient(Message.ERROR,
					"Nie jesteś polączony z żadnym klientem!");
			return;
		} else {
			pair.getWriter().sendMessage(m);
		}
	}

	private void SendToClient(byte msgType, String msg) {
		client.getWriter().sendMessage(new Message(msgType, msg.getBytes()));
	}

	/**
	 * {@link UpdateContactListListener#updateAvailableContact(String)}}
	 */
	@Override
	public void updateAvailableContact(String name) {
		SendToClient(Message.GET_AVAILABLE_CONTACT, name);
	}

	/**
	 * {@link UpdateContactListListener#updateLeavingContact(String)}}
	 */
	@Override
	public void updateLeavingContact(String name) {
		SendToClient(Message.GET_LEAVING_CONTACT, name);
	}
}